﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GalaSoft.MvvmLight;
using System.Threading;
using System.Collections.ObjectModel;
using GalaSoft.MvvmLight.Command;
using System.Diagnostics;
using System.Windows;
using System.IO;
using System.ComponentModel.DataAnnotations;
using System.Data;

namespace RazorSourceGenerator.ViewModel
{
    public class ConfigViewModel : PaneViewModel
    {
        private string CAPTION = "RazorSourceGenerator";

        private Generator _generator;
        private GeneratorConfig _config;
        private string _configFileName;

        private bool _isRunning = false;
        private CancellationTokenSource _cancelTokenSource;
        private bool _isDirty = false;
        private bool _isConnectionChanged = false;

        private int _selectedTemplateIndex;

        private System.Windows.Forms.FolderBrowserDialog _folderBrowserDialog;
        private SchemaInfoViewModel _schemaInfo;

        public SchemaInfoViewModel SchemaInfoViewModel
        {
            get { return _schemaInfo; }
            set
            {
                if (_schemaInfo != value)
                {
                    _schemaInfo = value;
                    RaisePropertyChanged("SchemaInfoViewModel");
                }
            }
        }

        public DataTable GlobalParameters
        {
            get;
            set;
        }

        public string Name
        {
            get
            {
                return Path.GetFileName(_configFileName);
            }
        }

        public new string Title
        {
            get
            {
                var configFileName = _configFileName;
                if (configFileName == null)
                    configFileName = "";
                if (configFileName.Length > 30)
                    configFileName = configFileName.Substring(0, 30) + "...";
                var title = string.Format("{0} {1} - {2}", configFileName, _isDirty ? "*" : "", CAPTION);
                RaisePropertyChanged("TitleAlias");
                return title;
            }
        }

        public string TitleAlias
        {
            get
            {
                var title2 = !string.IsNullOrEmpty(_configFileName) ?
                    string.Format("{0} {1}", Name, _isDirty ? "*" : "") : CAPTION;
                return title2;
            }
        }

        public bool IsDirty
        {
            get 
            { 
                return _isDirty; 
            }
            set
            { 
                _isDirty = value;
                RaisePropertyChanged("Title");
            }
        }

        public string ConfigFileName
        {
            get
            {
                return _configFileName;
            }
            set
            {
                if (_configFileName != value)
                {
                    _configFileName = value;
                    this.RaisePropertyChanged("ConfigFileName");
                    this.RaisePropertyChanged("Name");
                    this.RaisePropertyChanged("Title");
                }
            }
        }

        public GeneratorConfig GeneratorConfig
        {
            get 
            { 
                return _config; 
            }
            set 
            {
                _config = value;
                this.RaisePropertyChanged("GeneratorConfig");
            }
        }

        public bool IsRunning
        {
            get
            {
                return _isRunning;
            }
            set
            {
                if (_isRunning != value) { 
                    _isRunning = value;
                    this.RaisePropertyChanged("IsRunning");
                    this.RaisePropertyChanged("IsUIEnabled");
                }
            }
        }

        public bool IsUIEnabled
        {
            get
            {
                return !_isRunning;
            }
        }

        public Visibility UIVisibility
        {
            get
            {
                return Visibility.Hidden;
            }
        }

        public int SelectedTemplateIndex
        {
            get
            {
                return _selectedTemplateIndex;
            }
            set
            {
                _selectedTemplateIndex = value;
                RaisePropertyChanged("SelectedTemplateIndex");
            }
        }

        /// <summary>
        /// Initializes a new instance of the ConfigViewModel class.
        /// </summary>
        public ConfigViewModel()
        {
            if (IsInDesignMode)
            {
                // Design Data
                _config = new GeneratorConfig
                {
                    ConnectionString = "test db",
                    TableNames = "TableNames",
                    TableCatalog = "TableCatalog",
                    TableType = "TableType",
                    WorkDir = "WorkDir",
                    TableFilter = "TableFilter",
                    TableSchema = "Schema",
                    CommentSchema = "CommentSchema",
                    Templates = new ObservableCollection<TemplateConfig>
                    {
                        new TemplateConfig 
                        { 
                            Name="name1", OutputFileName="filename1", Enabled=true, Encoding="utf-8 without signature",
                            FileName="filename2", Append=false, ClearOutputDir=true
                        }
                    }
                };
                IsRunning = false;
                return;
            }
            else
            {
                this.Load();
            }

            _folderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();

        }

        public void Load(string fileName = null)
        {
           
            if (fileName == null)
            {
                
                GeneratorConfig = new GeneratorConfig();
                ConfigFileName = "Untitiled";
            }
            else
            {
                GeneratorConfig = GeneratorUtils.LoadConfig(fileName);
                ConfigFileName = fileName;
            }

            IsDirty = false;
            IsRunning = false;
  
            // listen the config's PropertyChanged event
            GeneratorConfig.PropertyChanged += GeneratorConfig_PropertyChanged;
            // listen the templates of config item changed event
            GeneratorConfig.Templates.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Templates_CollectionChanged);
            foreach(var template in GeneratorConfig.Templates)
                template.PropertyChanged += GeneratorConfig_PropertyChanged;

            _generator = new Generator(ConfigFileName, GeneratorConfig);
            SelectedTemplateIndex = 0;
        }

        void Templates_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            IsDirty = true;
        }

        void GeneratorConfig_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "ConnectionString")
                _isConnectionChanged = true;

            IsDirty = true;
        }

        public void Save(string fileName = null)
        {
            ConfigFileName = fileName == null ? _configFileName : fileName;
            GeneratorUtils.SaveConfig(_config, ConfigFileName);
            IsDirty = false;
        }

        public void SaveAs(string fileName, bool copyTemplate = true)
        {
            // copy templates
            var dir = System.IO.Path.GetDirectoryName(fileName);
            if (copyTemplate)
            {
                foreach (var template in _config.Templates)
                {
                    var srcFileName = GeneratorUtils.GetActualFileName(_config.WorkDir, template.FileName);
                    var destFileName = GeneratorUtils.GetActualFileName(dir, template.FileName);
                    var destDir = Path.GetDirectoryName(destFileName);
                    if (!Directory.Exists(destDir))
                        Directory.CreateDirectory(destDir);
                    File.Copy(srcFileName, destFileName);
                }
            }
            _config.WorkDir = dir;
            Save(fileName);
            Load(fileName);
        }

        public bool Validate()
        {
            var res = new List<ValidationResult>();
            var validate = Validator.TryValidateObject(_config, new ValidationContext(_config, null, null), res);
            if (validate)
            {
                validate = _config.Templates.ToList().All(t => Validator.TryValidateObject(t, new ValidationContext(t, null, null), res));
            }
            if (!validate)
            {
                Utils.ShowError("Invalid Input, please modify and try again.");
                return false;
            }
            return true;
        }

        #region Commands
        private RelayCommand _selectFolderCommand;
        public RelayCommand SelectFolderCommand
        {
            get
            {
                if (_selectFolderCommand == null)
                {
                    _selectFolderCommand = new RelayCommand(() =>
                    {
                        System.Windows.Forms.DialogResult result = _folderBrowserDialog.ShowDialog();
                        if (result == System.Windows.Forms.DialogResult.OK)
                        {
                            var orgWorkDir = _config.WorkDir != null ? _config.WorkDir.TrimEnd('\\') : "";
                            _config.WorkDir = _folderBrowserDialog.SelectedPath;
                            if (!string.IsNullOrEmpty(orgWorkDir) && orgWorkDir != _config.WorkDir)
                            {
                                Utils.ShowWarn("WorkDir has been changed.");
                                // check the template fileName whether is the relative path.
                                foreach (var template in _config.Templates)
                                {
                                    if (!Path.IsPathRooted(template.FileName))
                                        template.FileName = Path.Combine(orgWorkDir, template.FileName);
                                }
                            }
                        }
                    });
                }
                return _selectFolderCommand;
            }
        }

        private RelayCommand _openConnStrHelpCommand;
        public RelayCommand OpenConnStrHelpCommand
        {
            get
            {
                if (_openConnStrHelpCommand == null)
                {
                    _openConnStrHelpCommand = new RelayCommand(() =>
                    {
                        var dialog = new ConnectionStringHelp();
                        dialog.Owner = Application.Current.MainWindow;
                        var ret = dialog.ShowDialog();
                        if (ret.HasValue && ret.Value)
                        {
                            _config.ConnectionString = dialog.SelectedConnectionString;
                        }
                    });
                }
                return _openConnStrHelpCommand;
            }
        }

        private RelayCommand _runCommand;
        public RelayCommand RunCommand
        {
            get
            {
                if (_runCommand == null)
                {
                    _runCommand = new RelayCommand(() =>
                    {
                        if (!Validate())
                        {
                            return;
                        }

                        IsRunning = true;
                        _cancelTokenSource = new CancellationTokenSource();
                        _generator.GlobalParams = GlobalParameters;
                        var task = _generator.RunAsync(_cancelTokenSource.Token);
                        task.ContinueWith(t => IsRunning = false);
                        task.Start();
                    });
                }
                return _runCommand;
            }
        }

        private RelayCommand _stopCommand;
        public RelayCommand StopCommand
        {
            get {
                if (_stopCommand == null)
                {
                    _stopCommand = new RelayCommand(() =>
                    {
                        if (_cancelTokenSource != null)
                        {
                            _cancelTokenSource.Cancel(true);
                            IsRunning = false;
                        }
                    });
                }
                return _stopCommand;
            }
        }

        private RelayCommand<string> _openFileCommand;
        public RelayCommand<string> OpenFileCommand
        {
            get
            {
                if (_openFileCommand == null)
                {
                    _openFileCommand = new RelayCommand<string>(filename =>
                    {
                        if (string.IsNullOrEmpty(filename))
                        {
                            Utils.ShowError("FileName is empty.");
                            return;
                        }
                        if (string.IsNullOrEmpty(_config.WorkDir) || !Directory.Exists(_config.WorkDir))
                        {
                            Utils.ShowError("WorkDir is empty or not exists.");
                            return;
                        }
                        filename = GeneratorUtils.GetActualFileName(_config.WorkDir, filename);
                        var dir = Path.GetDirectoryName(filename);
                        if (!Directory.Exists(dir))
                            Directory.CreateDirectory(dir);
                        if (!File.Exists(filename))
                            File.WriteAllText(filename, "");
                        Process.Start("notepad.exe", "\"" + GeneratorUtils.GetActualFileName(_config.WorkDir, filename) + "\"");
                    });
                }
                return _openFileCommand;
            }
        }

        private RelayCommand<string> _openFolderCommand;
        public RelayCommand<string> OpenFolderCommand
        {
            get
            {
                if (_openFolderCommand == null)
                {
                    _openFolderCommand = new RelayCommand<string>(folder =>
                    {
                        if (string.IsNullOrEmpty(folder))
                        {
                            Utils.ShowError("FileName is empty.");
                            return;
                        }
                        if (string.IsNullOrEmpty(_config.WorkDir) || !Directory.Exists(_config.WorkDir))
                        {
                            Utils.ShowError("WorkDir is empty or not exists.");
                            return;
                        }
                        if (!Path.IsPathRooted(folder))
                        {
                            folder = System.IO.Path.Combine(_config.WorkDir, System.IO.Path.GetDirectoryName(folder));
                            if (!System.IO.Directory.Exists(folder))
                                System.IO.Directory.CreateDirectory(folder);
                        }
                        else if (folder != _config.WorkDir)
                        {
                            folder = System.IO.Path.GetDirectoryName(folder);
                        }
                        Process.Start("Explorer.exe", "\"" + folder + "\"");
                    });
                }
                return _openFolderCommand;
            }
        }

        private RelayCommand<TemplateConfig> _openSQLEditorCommand;
        public RelayCommand<TemplateConfig> OpenSQLEidtorCommand
        {
            get
            {
                if (_openSQLEditorCommand == null)
                {
                    _openSQLEditorCommand = new RelayCommand<TemplateConfig>(item =>
                    {
                        var editor = new SQLEidtor();
                        editor.Owner = Application.Current.MainWindow;
                        editor.txtSQL.Text = item.SQL;
                        editor.chkLoopInSQLResult.IsChecked = item.LoopInSQLResult;
                        var dialogResult = editor.ShowDialog();
                        if (dialogResult.HasValue && dialogResult.Value)
                        {
                            item.SQL = editor.txtSQL.Text;
                            item.LoopInSQLResult = editor.chkLoopInSQLResult.IsChecked.Value;
                        }
                    });
                }
                return _openSQLEditorCommand;
            }
        }

        private RelayCommand _addTemplateCommand;
        public RelayCommand AddTemplateCommand
        {
            get
            {
                if (_addTemplateCommand == null)
                {
                    _addTemplateCommand = new RelayCommand(() =>
                    {
                        _config.Templates.Add(new TemplateConfig 
                        { 
                            Name = "template_" + (_config.Templates.Count + 1),
                            Encoding = "utf-8 without signature",
                            FileName = "templates\\template_" + (_config.Templates.Count + 1) + ".txt",
                            OutputFileName = "output\\some_file_name"
                        });
                        SelectedTemplateIndex = _config.Templates.Count - 1;
                    });
                }
                return _addTemplateCommand;
            }
        }

        private RelayCommand<TemplateConfig> _deleteTemplateCommand;
        public RelayCommand<TemplateConfig> DeleteItemCommand
        {
            get
            {
                if (_deleteTemplateCommand == null)
                {
                    _deleteTemplateCommand = new RelayCommand<TemplateConfig>(item =>
                    {
                        var ret = Utils.ShowConfirmOKCancel("Delete this template \"{0}\" ?", item.Name);
                        if (ret == MessageBoxResult.OK)
                        {
                            _config.Templates.Remove(item);
                            var nextIndex = SelectedTemplateIndex - 1;
                            if (nextIndex < 0) nextIndex = 0;
                            SelectedTemplateIndex = nextIndex;
                        }
                    });
                }
                return _deleteTemplateCommand;
            }
        }

        private RelayCommand _testConnectionCommand;
        public RelayCommand TestConnectionCommand
        {
            get
            {
                if (_testConnectionCommand == null)
                {
                    _testConnectionCommand = new RelayCommand(() => {
                        try
                        {
                            _generator.TestConnection();
                            Utils.ShowInfo("Connected Successfully.");
                        }
                        catch (Exception ex)
                        {
                            Utils.ShowError(ex.Message);
                        }
                    });
                }
                return _testConnectionCommand;
            }
        }


        private SchemaInfoView _schemaInfoView = null;
        private RelayCommand _showSchemaInfoCommand;
        public RelayCommand ShowSchemaInfoCommand
        {
            get
            {
                if (_showSchemaInfoCommand == null)
                {
                    _showSchemaInfoCommand = new RelayCommand(() =>
                        {
                            try
                            {
                                //if (_schemaInfoView == null)
                                //{
                                    _schemaInfoView = new SchemaInfoView();
                                    _schemaInfoView.Owner = Application.Current.MainWindow;
                                //}
                                if (_schemaInfo == null || _isConnectionChanged)
                                {
                                    _schemaInfo = new SchemaInfoViewModel(_generator);
                                    _schemaInfo.Load();
                                    _isConnectionChanged = false;
                                }
                                _schemaInfoView.DataContext = _schemaInfo;
                                var ret = _schemaInfoView.ShowDialog();

                                if (_schemaInfo.TableSchemas != null && (ret.HasValue && ret.Value))
                                    _config.TableNames = string.Join(",", _schemaInfo.TableSchemas.Where(t => t.IsChecked).Select(t => t.TableName).ToArray());
                                
                            }
                            catch (Exception ex)
                            {
                                Utils.ShowError(ex.Message);
                                _schemaInfo = null;
                            }
                        });
                }
                return _showSchemaInfoCommand;
            }
        }
        #endregion
    }
}
